#pragma once

#include <GL/gl.h>
#include <GL/freeglut_std.h>
#include <cstdio>
#include "model.h"
#include "../gl/Drawabe.h"

struct LayerStepView {
    Steps step;
    Axis axis;
    int angle{};
    int current_angle;
    char layers;

    void assign(LayerStepMeta * src) {
        axis = src->axis;
        angle = src->angle;
        layers = src->layers;
    }

    void init() {
        layers = 0;
    }

    explicit LayerStepView(Steps step) {
        this->step = step;
        this->current_angle = 0;
    }

};


struct __attribute__((__packed__))  TriangleInfo {
    Vector3f normal;
    Vector3f vertex[3];
    uint16_t attrs;
};

class View {
private:
    Cube * model;
    const float cube_size = 1.0f;
    RotateValue view_rotation = {.angle=30, .vector = {.x = 0.1, .y = 0.1, .z = 0.2}};
    RotateValue view_rotation_inc  = {.angle=1, .vector = {.x = 0.2, .y = 0.3, .z = 0.02}};
    Vector3f translate = {.x = 0,.y = 0, .z = -8};

    void setColor(ColorIndex color) {
        Point3f * rgb;
        switch (color) {
            case ColorIndex::black:
                rgb = new Point3f(0.2, 0.2, 0.2);
                break;
            case ColorIndex::red:
                rgb = new Point3f(1, 0, 0);
                break;
            case ColorIndex::green:
                rgb = new Point3f(0, 1, 0);
                break;
            case ColorIndex::blue:
                rgb = new Point3f(0, 0, 1);
                break;
            case ColorIndex::yellow:
                rgb = new Point3f(1, 1, 0);
                break;
            case ColorIndex::white:
                rgb = new Point3f(1, 1, 1);
                break;
            case ColorIndex::orange:
                rgb = new Point3f(1, 0.6, 0);
                break;
            default:
                rgb = nullptr;
        }

        if (rgb) {
            glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, rgb->items);
            //glColor3f(rgb->f0, rgb->f1, rgb->f2);
            delete rgb;
        }
    }

    void draw_item(int x, int y, int z) {
        const float cs = cube_size/2;
        glPushMatrix();

        glTranslatef((x-1) * cube_size * 1.1, (y-1) * cube_size * 1.1, (z-1)* cube_size * 1.1);

        setColor(this->model->items[x][y][z]->colors.set.up);
        glBegin(GL_QUADS);                // Begin drawing the color cube with 6 quads
        // Top face (y = 1.0f)
        // Define vertices in counter-clockwise (CCW) order with normal pointing out
        //glNormal3f(0, -1, 0);
        glVertex3f( cs, cs, -cs);
        glVertex3f(-cs, cs, -cs);
        glVertex3f(-cs, cs,  cs);
        glVertex3f( cs, cs,  cs);
        glEnd();

        // Bottom face (y = -1.0f)
        setColor(this->model->items[x][y][z]->colors.set.down);
        glBegin(GL_QUADS);                // Begin drawing the color cube with 6 quads
        //glNormal3f(0, 1, 0);
        glVertex3f( cs, -cs,  cs);
        glVertex3f(-cs, -cs,  cs);
        glVertex3f(-cs, -cs, -cs);
        glVertex3f( cs, -cs, -cs);
        glEnd();

        // Front face  (z = 1.0f)
        setColor(this->model->items[x][y][z]->colors.set.front);
        glBegin(GL_QUADS);                // Begin drawing the color cube with 6 quads
        //glNormal3f(0, 0, 1);
        glVertex3f( cs,  cs, cs);
        glVertex3f(-cs,  cs, cs);
        glVertex3f(-cs, -cs, cs);
        glVertex3f( cs, -cs, cs);
        glEnd();

        // Back face (z = -cs)
        setColor(this->model->items[x][y][z]->colors.set.back);
        glBegin(GL_QUADS);                // Begin drawing the color cube with 6 quads
        //glNormal3f(0, 0, -1);
        glVertex3f( cs, -cs, -cs);
        glVertex3f(-cs, -cs, -cs);
        glVertex3f(-cs,  cs, -cs);
        glVertex3f( cs,  cs, -cs);
        glEnd();

        // Left face (x = -cs)
        setColor(this->model->items[x][y][z]->colors.set.left);
        glBegin(GL_QUADS);                // Begin drawing the color cube with 6 quads
        //glNormal3f(1, 0, 0);
        glVertex3f(-cs,  cs,  cs);
        glVertex3f(-cs,  cs, -cs);
        glVertex3f(-cs, -cs, -cs);
        glVertex3f(-cs, -cs,  cs);
        glEnd();

        // Right face (x = cs)
        setColor(this->model->items[x][y][z]->colors.set.right);
        glBegin(GL_QUADS);                // Begin drawing the color cube with 6 quads
        //glNormal3f(-1, 0, 0);
        glVertex3f(cs,  cs, -cs);
        glVertex3f(cs,  cs,  cs);
        glVertex3f(cs, -cs,  cs);
        glVertex3f(cs, -cs, -cs);
        glEnd();  // End of drawing color-cube

        glPopMatrix();
    }


    void color_cube() {
        if (layer_step == nullptr) {
            for (char z = 0; z < 3; z++)
                for (char y = 0; y < 3; y++)
                    for (char x = 0; x < 3; x++)
                        draw_item(x, y, z);
        } else {
            for (char a = 0; a < 3; a++) {
                glPushMatrix();
                if (layer_step->layers & (1<<a)) {
                    switch (layer_step->axis) {
                        case A_Z:
                            glRotatef(layer_step->current_angle, 0, 0, 1);
                            break;
                        case A_X:
                            glRotatef(layer_step->current_angle, 1, 0, 0);
                            break;
                        case A_Y:
                            glRotatef(layer_step->current_angle, 0, 1, 0);
                            break;
                    }
                }
                for (char b = 0; b < 3; b++) {
                    for (char c = 0; c < 3; c++) {
                        char x, y, z;
                        switch (layer_step->axis) {
                            case A_Z:
                                x = b; y = c; z = a;
                                break;
                            case A_X:
                                x = a; y = c; z = b;
                                break;
                            case A_Y:
                                x = b; y = a; z = c;
                                break;
                        }
                        draw_item(x, y, z);
                    }
                }
                glPopMatrix();
            }
            layer_step->current_angle += 6 * (layer_step->angle>0? 1 : -1);
            if (abs(layer_step->current_angle) > abs(layer_step->angle)) {
                this->model->execStep(layer_step->step);
                delete layer_step;
                layer_step = nullptr;
            }
        }
    }
    LayerStepView * layer_step = nullptr;

public:

    void startMove(Steps step) {
        if (layer_step) {
            delete layer_step;
            layer_step = nullptr;
        }
        if (step!=Steps::ST_NONE) {
            layer_step = new LayerStepView(step);
            layer_step->assign(&step_meta[step]);
        }
    }

    bool isMoved() {
        return layer_step;
    }

    explicit View(Cube * model) {
        this->model = model;
    }

    void inc_rotation() {
        this->view_rotation+=this->view_rotation_inc;
    }

    void inc_translate(bool forward) {
        if (forward) {
            this->translate.z -= 1;
        } else
            this->translate.z += 1;
    }

    void draw() {

        glPushMatrix();
            glTranslatef(translate.x, translate.y, translate.z);  // Move right and into the screen
            glRotatef(view_rotation.angle, view_rotation.vector.x, view_rotation.vector.y, view_rotation.vector.z);

            color_cube();
        glPopMatrix();
    }
};